﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;

namespace Helpers
{
    public static class SwaggerExtensions
    {
        /// <summary>
        /// 生成带Bearer授权的接口文档
        /// </summary>
        public static IServiceCollection AddSwaggerz(this IServiceCollection services, IConfiguration configuration, Action<SwaggerzOptions> configureSwaggerzOptions = null, Action<SwaggerGenOptions> confirureSwaggerGenOptions = null)
        {
            var options = configuration.GetSection(SwaggerzOptions.SectionName).Get<SwaggerzOptions>() ?? new SwaggerzOptions();
            configureSwaggerzOptions?.Invoke(options);

            services.AddOptions<SwaggerzOptions>()
                .Bind(configuration.GetSection(SwaggerzOptions.SectionName))
                .ValidateDataAnnotations();
            services.PostConfigure<SwaggerzOptions>(x =>
            {
                configureSwaggerzOptions?.Invoke(x);
            });
            services.AddSingleton<SwaggerzOptions>(x => x.GetRequiredService<IOptions<SwaggerzOptions>>().Value);

            services.AddSwaggerGen(c =>
            {
                c.CustomSchemaIds(type => type.FullName);
                c.OrderActionsBy(x => x.RelativePath);
                //添加注释文档
                if (options.IncludeXmlComments == null) options.IncludeXmlComments = new List<string>();
                if (!options.IncludeXmlComments.Contains(Assembly.GetEntryAssembly().GetName().Name + ".xml")) options.IncludeXmlComments.Add(Assembly.GetEntryAssembly().GetName().Name + ".xml");
                foreach (var item in options.IncludeXmlComments)
                {
                    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, item), true);
                }

                c.OrderActionsBy(x => x.RelativePath);
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "接口文档", Version = "v1" });

                c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Description = "请输入Token(需要带有Bearer)",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                });
                //Json Token认证方式，此方式为全局添加
                c.AddSecurityRequirement(new OpenApiSecurityRequirement() {
                    {
                        new OpenApiSecurityScheme {
                        Reference = new OpenApiReference(){
                            Id = "Bearer",
                            Type = ReferenceType.SecurityScheme
                        } },
                        Array.Empty<string>() }
                });

                confirureSwaggerGenOptions?.Invoke(c);
            });
            services.AddSingleton<IStartupFilter, SwaggerStartupFilter>();
            return services;
        }

        /// <summary>
        /// 路径 docz/index.html
        /// </summary>
        /// <param name="pathBase">不需要带/</param>
        internal static IApplicationBuilder UseSwaggerz(this IApplicationBuilder app)
        {
            var options = app.ApplicationServices.GetRequiredService<SwaggerzOptions>();

            app.UseSwagger(x =>
            {
                x.RouteTemplate = $"{options.routePrefix}/{{documentName}}/swagger.json";
                options.ConfigareSwaggerOptions?.Invoke(x);
            });

            app.UseSwaggerUI(c =>
            {
                var url = string.IsNullOrEmpty(options.pathBase) ? $"/{options.routePrefix}/v1/swagger.json" : $"/{options.pathBase}/{options.routePrefix}/v1/swagger.json";
                c.SwaggerEndpoint(url, "api v1");
                c.RoutePrefix = $"{options.routePrefix}";

                c.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.List);

                options.ConfigareSwaggerUIOptions?.Invoke(c);
            });
            return app;
        }
    }
}
